package conf

import (
	"image"
	"image/color"
	"image/png"
	"math"
	"os"
	"svr/src/logs"
	"svr/src/xutil"
	"time"
)

type Grad struct {
	x float64
	y float64
	z float64
}

func (g *Grad) dot2(x, y float64) float64 {
	return g.x*x + g.y*y
}

func (g *Grad) dot3(x, y, z float64) float64 {
	return g.x*x + g.y*y + g.z*z
}
func NewGrad(x, y, z int) *Grad {
	return &Grad{x: float64(x), y: float64(y), z: float64(z)}
}

var grad3 = []*Grad{
	NewGrad(1, 1, 0),
	NewGrad(-1, 1, 0),
	NewGrad(1, -1, 0),
	NewGrad(-1, -1, 0),
	NewGrad(1, 0, 1),
	NewGrad(-1, 0, 1),
	NewGrad(1, 0, -1),
	NewGrad(-1, 0, -1),
	NewGrad(0, 1, 1),
	NewGrad(0, -1, 1),
	NewGrad(0, 1, -1),
	NewGrad(0, -1, -1),
}
var p = []int32{
	151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240,
	21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88,
	237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83,
	111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216,
	80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186,
	3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58,
	17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
	129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193,
	238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157,
	184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128,
	195, 78, 66, 215, 61, 156, 180,
}
var perm []int32
var gradP []*Grad

func NewPerlinNoise(seed int32) int32 {
	perm = make([]int32, 512)
	gradP = make([]*Grad, 512)

	if seed < 256 {
		seed |= seed << 8
	}
	for i := 0; i < 256; i++ {
		var v int32
		if i&1 != 0 {
			v = p[i] ^ (seed & 255)
		} else {
			v = p[i] ^ ((seed >> 8) & 255)
		}
		perm[i] = v
		perm[i+256] = v
		g3 := grad3[v%12]
		gradP[i] = g3
		gradP[i+256] = g3
	}
	return seed
}

var tileColors = []color.RGBA{
	color.RGBA{R: 0, G: 99, B: 199, A: 255},    //0 深海
	color.RGBA{R: 0, G: 160, B: 238, A: 255},   //1 浅海
	color.RGBA{R: 115, G: 188, B: 211, A: 255}, //2 海边
	color.RGBA{R: 229, G: 216, B: 184, A: 255}, //3 沙滩
	color.RGBA{R: 188, G: 147, B: 99, A: 255},  //4 土地
	color.RGBA{R: 81, G: 138, B: 20, A: 255},   //5 草地
	color.RGBA{R: 61, G: 97, B: 18, A: 255},    //6 树林
	color.RGBA{R: 77, G: 61, B: 53, A: 255},    //7 密林
	color.RGBA{R: 180, G: 180, B: 180, A: 255}, //8 山
	color.RGBA{R: 250, G: 250, B: 250, A: 255}, //9 山顶
}

func init() {
	addTile(TileDeepSea, 30)       //0 深海
	addTile(TileSea, 40)           //1 浅海
	addTile(TileShallowSea, 12)    //2 海边
	addTile(TileSand, 10)          //3 沙滩
	addTile(TileLand, 10)          //4 土地
	addTile(TileGrass, 30)         //5 草地
	addTile(TileFortrees, 22)      //6 树林
	addTile(TileDenseFortress, 18) //7 密林
	addTile(TileMoutain, 15)       //8 山
	addTile(TileMoutainTop, 10)    //9 山顶
}

const TileDeepSea int32 = 0
const TileSea int32 = 1
const TileShallowSea int32 = 2
const TileSand int32 = 3
const TileLand int32 = 4
const TileGrass int32 = 5
const TileFortrees int32 = 6
const TileDenseFortress int32 = 7
const TileMoutain int32 = 8
const TileMoutainTop int32 = 9

var tilesPercent []int32

func addTile(typ int32, count int) {
	for i := 0; i < count; i++ {
		tilesPercent = append(tilesPercent, typ)
	}
}

var NoiseMapTile [][]int32

func generateNoiseMap(w, h, seed, scale, octaves int32, persistance, lacunarity, offX, offY float64) *image.RGBA {
	side := w >> 4
	w += side
	h += side
	noiseMapOriginalF := make([][]float64, h)
	for i := 0; i < len(noiseMapOriginalF); i++ {
		noiseMapOriginalF[i] = make([]float64, w)
	}
	seed = NewPerlinNoise(seed)
	fw := float64(w)
	fh := float64(h)
	fsl := float64(scale)
	maxNoiseHeight := math.Inf(-1)
	minNoiseHeight := math.Inf(1)
	halfWidth := fw / 2
	halfHeight := fh / 2
	//原始数值地图
	for y := int32(0); y < h; y++ {
		for x := int32(0); x < w; x++ {
			amplitude := float64(1)
			frequency := float64(1)
			noiseHeight := float64(0)
			for i := int32(0); i < octaves; i++ {
				sampleX := ((float64(x) - halfWidth + offX*fw) / fsl) * frequency
				sampleY := ((float64(y) - halfHeight + offY*fh) / fsl) * frequency
				perlinValue := noise(sampleX, sampleY)*2 - 1
				noiseHeight += perlinValue * amplitude
				amplitude *= persistance
				frequency *= lacunarity
			}

			if noiseHeight > maxNoiseHeight {
				maxNoiseHeight = noiseHeight
			} else if noiseHeight < minNoiseHeight {
				minNoiseHeight = noiseHeight
			}
			noiseMapOriginalF[y][x] = noiseHeight
		}
	}
	w -= side
	h -= side
	Width = w
	Height = h
	adjustMapSide(noiseMapOriginalF, w, h, side) // 调整地图边缘，使上下左右连续

	//百分比数值地图
	noiseMapPercentF := make([][]float64, h)
	for i := 0; i < len(noiseMapPercentF); i++ {
		noiseMapPercentF[i] = make([]float64, w)
	}
	for y := int32(0); y < h; y++ {
		for x := int32(0); x < w; x++ {
			noiseMapPercentF[y][x] = invertLerp(minNoiseHeight, maxNoiseHeight, noiseMapOriginalF[y][x])
		}
	}

	//砖块ID地图
	NoiseMapTile = make([][]int32, h)
	for i := 0; i < len(NoiseMapTile); i++ {
		NoiseMapTile[i] = make([]int32, w)
	}
	lv := len(tilesPercent)
	img := image.NewRGBA(image.Rect(0, 0, int(w), int(h)))
	for y := int32(0); y < h; y++ {
		for x := int32(0); x < w; x++ {
			v := int(noiseMapPercentF[y][x] * float64(lv))
			if v > lv-1 {
				v = lv - 1
			}
			NoiseMapTile[y][x] = tilesPercent[v]
		}
	}

	// adjustMapRiver(seed)

	//生成图片
	for y := int32(0); y < h; y++ {
		for x := int32(0); x < w; x++ {
			img.Set(int(x), int(y), tileColors[NoiseMapTile[y][x]])
		}
	}
	return img
}

// ------------------------------------------------------------------------------------------
var Width, Height, RectSide, Rows, Cols, Seed int32
var MapRiverRect map[int32]bool

// 调整地图，增加河流，rectSide越小，河流越密集
func adjustMapRiver(seed int32) {
	Seed = seed
	RectSide = 64
	Rows = Height / RectSide
	Cols = Width / RectSide
	MapRiverRect = make(map[int32]bool)
	for r := int32(0); r < Rows; r++ {
		for c := int32(0); c < Cols; c++ {
			sx := c * RectSide
			ex := sx + RectSide
			sy := r * RectSide
			ey := sy + RectSide
			if findFortreesTile(sx, ex, sy, ey) {
				k := (r << 16) | c
				MapRiverRect[k] = true
			}
		}
	}
	for len(MapRiverRect) > 10 {
		for k, _ := range MapRiverRect {
			row := k >> 16
			col := k & 0xffff
			createRiver(row, col)
			break
		}
	}
}

const (
	dirNull      int32 = 0
	dirUp        int32 = 1
	dirUpRight   int32 = 2
	dirRight     int32 = 3
	dirDownRight int32 = 4
	dirDown      int32 = 5
	dirDownLeft  int32 = 6
	dirLeft      int32 = 7
	dirUpLeft    int32 = 8
)

var allDirs = []int32{dirNull, dirUp, dirUpRight, dirRight, dirDownRight, dirDown, dirDownLeft, dirLeft, dirUpLeft}
var roundDirs = []int32{dirUp, dirUpRight, dirRight, dirDownRight, dirDown, dirDownLeft, dirLeft, dirUpLeft}
var dirPos = [][]int32{
	{0, 0},
	{0, -1},
	{1, -1},
	{1, 0},
	{1, 1},
	{0, 1},
	{-1, 1},
	{-1, 0},
	{-1, -1},
}

func createRiver(row, col int32) {
	sx := col * RectSide
	ex := sx + RectSide
	sy := row * RectSide
	ey := sy + RectSide
	var midX, midY int32
	isFind := false
	for y := sy; y < ey; y++ {
		for x := sx; x < ex; x++ {
			if NoiseMapTile[y][x] == TileFortrees {
				setRiverTiles(x, y, allDirs)
				midX = x
				midY = y
				isFind = true
				break
			}
		}
		if isFind {
			break
		}
	}
	dir0 := xutil.GetRandomBySeed(&Seed, 1, 9)
	dir1 := dir0 + 4
	if dir1 > 8 {
		dir1 -= 8
	}
	x0, y0 := getPos(midX, midY, dir0)
	findRiver(x0, y0, dir0)
	x1, y1 := getPos(midX, midY, dir1)
	findRiver(x1, y1, dir1)

}

func findRiver(x, y, dir int32) {
	for {
		dirs := []int32{dir}
		rnd := xutil.GetRandomBySeed(&Seed, 0, 4)
		switch rnd {
		case 0:
			dirs = append(dirs, addDir(dir, 1))
			break
		case 1:
			dirs = append(dirs, addDir(dir, -1))
			break
		default:
			dirs = append(dirs, addDir(dir, 1), addDir(dir, -1))
			break
		}
		if setRiverTiles(x, y, dirs) <= 1 {
			return
		}
		rnd = xutil.GetRandomBySeed(&Seed, 2, 6)
		for i := int32(0); i < rnd; i++ {
			dirs = append(dirs, dir)
		}
		newDir := xutil.GetRandomValueBySeed(&Seed, dirs)
		x, y = getPos(x, y, newDir)
		dir = newDir
	}
}

func addDir(dir, val int32) int32 {
	dir += val
	if dir > 8 {
		dir -= 8
	} else if dir < 1 {
		dir += 8
	}
	return dir
}

func setRiverTiles(x0, y0 int32, dirs []int32) int32 {
	count := int32(0)
	for _, dir := range dirs {
		if setRiverOneTile(x0, y0, dir) {
			count++
		}
	}
	return count
}

func setRiverOneTile(x0, y0, dir int32) bool {
	x, y := getPos(x0, y0, dir)
	if NoiseMapTile[y][x] != TileSea {
		NoiseMapTile[y][x] = TileSea
		col := x / RectSide
		row := y / RectSide
		k := (row << 16) | col
		if _, ok := MapRiverRect[k]; ok {
			delete(MapRiverRect, k)
		}
		return true
	}
	return false
}

func getPos(x0, y0, dir int32) (int32, int32) {
	pos := dirPos[dir]
	x := x0 + pos[0]
	y := y0 + pos[1]
	if x >= Width {
		x -= Width
	} else if x < 0 {
		x += Width
	}
	if y >= Height {
		y -= Height
	} else if y < 0 {
		y += Height
	}
	return x, y
}

// 寻找面积大于三分之一的森林
func findFortreesTile(sx, ex, sy, ey int32) bool {
	max := RectSide * RectSide / 3
	ct := int32(0)
	for y := sy; y < ey; y++ {
		for x := sx; x < ex; x++ {
			if NoiseMapTile[y][x] == TileFortrees {
				ct++
				if ct > max {
					return true
				}
			}
		}
	}
	return false
}

// func findRect(noiseMapTile [][]int32, mapRiverRect map[int32]bool, rows, cols, rectSide int32) (int32, int32) {
// 	for r := int32(0); r < rows; r++ {
// 		for c := int32(0); c < cols; c++ {
// 			k := (r << 16) | c
// 			if _, ok := mapRiverRect[k]; !ok {

// 			}
// 		}
// 	}

// }

// ------------------------------------------------------------------------------------------
// 调整地图边缘，使上下左右连续
func adjustMapSide(noiseMapF [][]float64, w, h, side int32) {
	outX := w
	outY := h
	outW := w + side
	outH := h + side
	s := float64(side)
	for y := int32(0); y < outH; y++ {
		x := int32(0)
		for ox := outX; ox < outW; ox++ {
			vl := noiseMapF[y][x]
			vr := noiseMapF[y][ox]
			noiseMapF[y][x] = vl*float64(x)/s + vr*(s-float64(x))/s
			x++
		}
	}
	for x := int32(0); x < outW; x++ {
		y := int32(0)
		for oy := outY; oy < outH; oy++ {
			vu := noiseMapF[y][x]
			vd := noiseMapF[oy][x]
			noiseMapF[y][x] = vu*float64(y)/s + vd*(s-float64(y))/s
			y++
		}
	}
}

// ------------------------------------------------------------------------------------------
func invertLerp(min, max, value float64) float64 {
	return (value - min) / (max - min)
}
func noise(x, y float64) float64 {
	X := int32(math.Floor(x))
	Y := int32(math.Floor(y))
	x = x - float64(X)
	y = y - float64(Y)
	X = X & 255
	Y = Y & 255
	n00 := gradP[X+perm[Y]].dot2(x, y)
	n01 := gradP[X+perm[Y+1]].dot2(x, y-1)
	n10 := gradP[X+1+perm[Y]].dot2(x-1, y)
	n11 := gradP[X+1+perm[Y+1]].dot2(x-1, y-1)
	u := fade(x)
	return lerp(lerp(n00, n10, u), lerp(n01, n11, u), fade(y))
}

func lerp(a, b, t float64) float64 {
	return (1-t)*a + t*b
}

func fade(t float64) float64 {
	return t * t * t * (t*(t*6-15) + 10)
}

// ------------------------------------------------------------------------------------------
func runBolin() {
	logs.Debugf("runBolin in----------")
	// 设置图像大小
	seed := int32(1)
	w := int32(1024 * 2)
	h := int32(1024 * 2)
	scale := int32(200)
	octaves := int32(5)
	persistance := float64(0.4)
	lacunarity := float64(3)
	offX := float64(0)
	offY := float64(0)

	img := generateNoiseMap(w, h, seed, scale, octaves, persistance, lacunarity, offX, offY)
	// 生成纹理图
	// img := generateTexture(w, h)

	// 保存为PNG文件
	file, err := os.Create("../../log/map" + time.Now().Format("2006_01_02 15.04.05") + ".png")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	err = png.Encode(file, img)
	if err != nil {
		panic(err)
	}
	logs.Debugf("runBolin out----------")
}
